﻿using System;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;

namespace SimpleORM
{
    internal class QueryContextProvider : IQueryProvider
    {
        #region Fields

        private QueryContext instance;

        #endregion

        #region Ctor

        internal QueryContextProvider(QueryContext qc)
        {
            instance = qc;
        }

        #endregion

        #region Methods

        #region Methods - Query

        public IQueryable<T> CreateQuery<T>(Expression expression)
        {
            QueryContext qc = new QueryContext<T>(instance.DataSource, expression);
            qc.Settings = instance.Settings;
            qc.CommandContext = instance.CommandContext.Clone() as ICommandContext;

            //return QuerybaseAOP.Create(qc) as IQueryable<T>;
            IQueryable<T> q = qc as IQueryable<T>;
            return q;
        }

        public IQueryable CreateQuery(Expression expression)
        {
            ExpressionTranslater translater = instance.DataSource.CreateExpressionTranslater();
            translater.Translate(expression);

            Type type = typeof(QueryContext<>).
                MakeGenericType(translater.ResultObjectContext.ElementType);

            QueryContext qc = (QueryContext)Activator.CreateInstance(
                type, instance.DataSource, expression);

            qc.Settings = instance.Settings;
            qc.CommandContext = instance.CommandContext.Clone() as ICommandContext;
            return qc;
            //return QuerybaseAOP.Create(qc) as IQueryable;
        }

        public T Execute<T>(Expression expression)
        {
            Type type = typeof(T);
            object result = Execute(expression, type.IsEnumerable() &&
                                                !(type.IsKindOf(typeof(string))));

            if (result == null)
                return default(T);

            return (T)result;
        }

        public object Execute(Expression expression)
        {
            return Execute(expression, false);
        }

        #endregion

        #region Methods - Private

        private object ExecuteNoTimer(Expression expression, bool IsEnumerable)
        {
            ExpressionTranslater translater = instance.DataSource.CreateExpressionTranslater();
            translater.Translate(expression);

            QueryContext qcInstance = instance.CloneInstance() as QueryContext;
            qcInstance.CommandContext = translater.Result;
            qcInstance.CommandContext.Parameters.Clear();

            IQueryContext qc = qcInstance;// (IQueryContext)QuerybaseAOP.Create(qcInstance);

            if (translater.ResultType == eExpressionResultType.Number ||
                translater.ResultType == eExpressionResultType.Bool)
            {
                object value = instance.DataSource.CreateDataReader().
                    TypeConverter.To(qc.Execute(),
                    translater.ResultObjectContext.ElementType);

                return value;
            }

            IList objs = qc.GetObjects(translater.ResultObjectContext.ElementType);
            if (objs == null)
                return null;

            if (translater.ResultType == eExpressionResultType.Element)
            {
                if (objs.Count == 0)
                    return translater.ResultObjectContext.ElementType.GetDefaultValue();
                else
                    return objs[0];
            }

            IQueryable qcData = objs.AsQueryable();

            Expression newExpression = new ExpressionCheater(qcData,
                typeof(QueryContext<>).MakeGenericType(
                translater.ElementObjectContext.ElementType)).
                Cheat(translater.Expression);

            if (IsEnumerable)
                return qcData.Provider.CreateQuery(newExpression);
            else
                return qcData.Provider.Execute(newExpression);
        }

        private object Execute(Expression expression, bool IsEnumerable)
        {
            //if (!instance.Settings.PrintExecuteTime)
            //return ExecuteNoTimer(expression, IsEnumerable);

            var sw = System.Diagnostics.Stopwatch.StartNew();
            try
            {
                return ExecuteNoTimer(expression, IsEnumerable);
            }
            finally
            {
                sw.Stop();
                //Tracker.WriteLine(sw.Elapsed.ToString());
            }
        }

        #endregion

        #endregion
    }

}
